<template>
	<!-- 试题管理--添加试题 -->
	<div class="card-wrapper " style="padding-bottom: 80px;">
		<!-- 标题 -->
		
		<h4 class="edit-page-title"><span >添加试题</span>
			<div class="float-right edit-page-tag">标<span style="color:red;margin: 0 3px;">*</span>为必填项</div>
		</h4>
		<!-- 按钮组 -->
		<div class="d-flex align-items-center justify-content-between stgl-whsq-tool-wrap">
			<!-- <div>试题完成情况</div> -->
			<div>当前已添加&nbsp;&nbsp;{{formList.length}}&nbsp;&nbsp;题</div>
			<div class="stgl-whsq-btn-group">
				<button type="button" class="btn btn-outline-primary stgl-whsq-btn" @click="manageUser">
					<img src="./static/img/question/mange-user-icon.png" />
					<!-- <i class="ivu-icon ivu-icon-md-settings"></i> -->
					管理出题人
				</button>
				<button type="button"class="btn btn-outline-primary stgl-whsq-btn" @click="globalConfig">
					<!-- <i class="ivu-icon ivu-icon-md-settings"></i> -->
					<img src="./static/img/question/global-config-icon.png" />
					配置全局属性
				</button>
			</div>
		</div>
		<!-- 内容 -->
		<div class="stgl-whsq-wrapper d-flex ">
			<div style="min-width: 260px;">
				<div class="stgl-whsq-bar" ref="stgl-bar">
					<div class="stgl-whsq-h">编辑工具</div>
					<div>
						<i-menu theme="light" :accordion="true" :open-names="[barArr[0].id]">
							<template v-if="barArr[0]">
								<!-- 试题列表 -->
								<Submenu :name="barArr[0].id">
									<!-- 主菜单 -->
									<template slot="title"><img class="menu-item-title-icon" src="./static/img/question/add-normal-icon.png" />{{barArr[0].title}}</template>
									<menu-item :name="e.id" v-for="e,i in barArr[0].children" :key="i">
										<div class=" menu-item-li d-flex justify-content-between flag-poptip-wrapper">
											<span class="menu-title" @click="menuTitleClick(e,'1')"><Icon type="md-add" class="com-mr10"></Icon>{{e.title}}</span>
											<div class="flag-poptip-select poptip-right" v-if="flagMap[e.param] && flagMap[e.param].filter((e)=>{return e.flag}).length>1">
												<ul>
													<li @click.stop="menuClick('1',e,typeVal.flagAlias)" v-for="typeVal of questionTypeVo(e.param)" :key="typeVal.id">
														{{typeVal.name}}
													</li>
												</ul>
											</div>
										</div>
									</menu-item>
								</Submenu>
							</template>
							<template v-if="barArr[1]">
								<!-- 试题列表 -->
								<Submenu :name="barArr[1].id">
									<!-- 主菜单 -->
									<template slot="title"><img class="menu-item-title-icon" src="/static/img/question/add-compound-icon.png" />{{barArr[1].title}}</template>
									<menu-item name="9-9">
										<div class=" menu-item-li d-flex justify-content-between" @click="addQuestionFromTem()">
											<span class="menu-title" ><Icon type="md-add" class="com-mr10"></Icon>复合题模板</span>
											<!-- <div class="question-template-wrapper">
												<div class="question-template-container">
													<img @click="addQuestionFromTem(item)" :src="item.src" alt="" v-for="item in templateImgList">
												</div>
											</div> -->
										</div>
									</menu-item>
									<menu-item :name="e.id" v-for="e,i in barArr[1].children" :key="i">
										<div class=" menu-item-li d-flex justify-content-between flag-poptip-wrapper">
											<span class="menu-title" @click="menuTitleClick(e,'2')"><Icon type="md-add" class="com-mr10"></Icon>{{e.title}}</span>
											<div class="flag-poptip-select poptip-right" v-if="flagMap[e.param] && flagMap[e.param].filter((e)=>{return e.flag}).length>1">
												<ul>
													<li @click="menuClick('2',e,typeVal.flagAlias)" v-for="typeVal of questionTypeVo(e.param)" :key="typeVal.id">
														{{typeVal.name}}
													</li>
												</ul>
											</div>
											
										</div>
									</menu-item>
								</Submenu>
							</template>
							<template v-if="barArr[2]">
								<!-- 素材列表 -->
								<Submenu :name="barArr[2].id">
									<!-- 主菜单 -->
									<template slot="title"><img class="menu-item-title-icon" src="./static/img/question/add-img-icon.png" />{{barArr[2].title}}</template>
									<img-material></img-material>
									<!-- <menu-item name="10-10">
										<div class=" menu-item-li d-flex justify-content-between">
											<span class="menu-title" @click="uploadImg(barArr[2])"><Icon type="md-add" class="com-mr10"></Icon>上传图片素材</span>
										</div>
									</menu-item> -->
									<menu-item name="10-11" >
										<div class="insert-img">
											<div v-for="e,i in barArr[2].children" :key="i" class="item-img">
												<img :src="e.param" alt="" style="max-width: 100%;" ></img>
												<div class="insert-img-zhezhao row">
													<div @click="removeFile(e,i,barArr[2].children)" >
														<Icon type="ios-trash-outline" size="14" color="#fff"></Icon>
														<span class="remove-file">移除</span>
													</div>
												</div>
											</div>
											
										</div>
									</menu-item>
								</Submenu>
							</template>
							<Submenu :name="item.id" v-for="item,index in barArr.slice(3)" :key="index">
								<template slot="title">
									<img class="menu-item-title-icon" :src="'./static/img/question/'+item.icon" />{{item.title}}
								</template>
								<menu-item :name="e.id" v-for="e,i in item.children" :key="i">
									<div class="position-relative menu-item-li d-flex justify-content-between">
										<span @click="menuClick(item.type,e)" class="menu-title">
											<Icon type="md-add" class="com-mr10"></Icon>
											{{e.title}}
										</span>
										<my-upload v-if="item.type == 5" :config="importConfig" :multiple="false" @upload-change="changeImportFile"
											class="stgl-whsq-upload">
											<div class="flex-fill menu-img-upload-btn">
												<img src="/static/img/question/img-upload-icon.png" />
												上传图片素材
											</div>
										</my-upload>
									</div>
								</menu-item>
							</Submenu>
						</i-menu>
						
					</div>
				</div>
			</div>
			
			
			<div class="stgl-whsq-content flex-fill">
				<!-- <score-percent></score-percent> -->
				<div class="d-flex align-items-center justify-content-between stgl-whsq-h">
					<div class="stgl-tab-title-left">
						<a :class="['stgl-tab-title-link', {'active': mode == 'normal'}]" href="javascript:;">常规模式</a>
						<!-- <a :class="['stgl-tab-title-link', {'active': mode == 'normal'}]" @click="mdShowM1('normal')" href="javascript:;">常规模式</a> -->
						<!-- <a :class="['stgl-tab-title-link', {'active': mode == 'md'}]" @click="mdShowM1('md')" href="javascript:;">MD模式</a> -->
					</div>
					<div class="stgl-tab-title-right"></div>
				</div>
					<section v-show="mode == 'normal'">
					<div v-for="item,index in formList" :key="item._id" style="position: relative;z-index: 1;">
						<div :id="`blankAnswerEl-${item.id}`" hidden></div>
						<div :id="`blankTitleEl-${item.id}`" hidden></div>
						<div v-if="item.flag!='9'" @click="formClick(item)" :class="{'box-shadow-active':isFocus==item._id}" v-show="!item.show">
							
							<paper 
								:index="index+1"  
								:flag-list="flagMap[item.flag]" 
								:user-map="userMap" 
								:expression="expression" 
								@delete-emit="deleteEmit" 
								@click-math="clickMath" 
								@copy-emit="copyEmit"
								:ref="'form'+index"
								:id="`question-item-${item._id}`"
								:vo="item"
								:is-import="item.isImport"
								:key="item._id">
							</paper>
						</div>
						<div v-else @click="isFocus=item._id" :class="{'box-shadow-active':isFocus==item._id}">
							<compound 
								:index="index+1"
								:isfocus.sync="isFocus" 
								:user-map="userMap"
								:expression="expression" 
								@delete-emit="deleteEmit" 
								@copy-emit="copyEmit"
								:ref="'form'+index"
								:id="`question-item-${item._id}`"
								@click-math="clickMath" 
								:vo="item" 
								:flag-map-list="flagMap"
								:is-edit="item.isImport"
								:is-import="item.isImport"
								:key="item._id">
							</compound>
						</div>
					</div>
					<div class="no-data-qusetion" v-show="formList.length==0"></div>
				</section>
				
					<div class="common-table-wrap contentedit" v-show="mode != 'normal'">
						  <div class="parent d-flex">
						    <div class="left flex-fill" style="max-width: 50%;">
						      <div id="editor" ></div>
						    </div>
						    <textarea class="flex-fill" id="source" v-model="mdText" class="right" readonly></textarea>
						  </div>
					</div>
			</div>
		</div>
		<div style="position: fixed;bottom: 30px;z-index: 99;width: auto;left: 42%;" class="com-btn-footer-wrapper">
			<button type="submit" class="btn btn-primary common-form-btn" @click="save">保存</button>
			<button type="submit" class="btn btn-primary common-form-btn form-btn" @click="download">打包下载</button>
			<button type="button" class="btn common-form-btn-close" @click="reset">重置</button>
			<button type="button" class="btn common-form-btn-close" @click="closePage">关闭当前页</button>
		</div>
		<!-- 试题序号分组 -->
		<div v-if="allGroupData" @mouseleave="overGroupVo=null" class="d-flex flex-column justify-content-center stgl-whsq-group-wrap">
			<template v-if="Array.isArray(allGroupData)">
				<div v-for="(item, index) in allGroupData" :key="index" @mouseover="overGroupVo = allGroupData[index]" :class="['d-flex flex-column justify-content-center position-relative stgl-whsq-group-item', {'active': overGroupVo && (overGroupVo.minNum == item.minNum && overGroupVo.maxNum == item.maxNum)}]">
					<span>{{item.minNum}}</span>
					<span v-if="item.minNum != item.maxNum" class="stgl-whsq-group-ellipse">...</span>
					<span v-if="item.minNum != item.maxNum">{{item.maxNum}}</span>
					<span v-if="item.validErrorNum" class="stgl-whsq-group-error-num">{{item.validErrorNum}}</span>
				</div>
				<!--试题序号详情-->
				<div v-if="overGroupVo" class="postion-absolute stgl-whsq-group-list">
					<a v-for="indexItem in overGroupVo.maxNum-overGroupVo.minNum+1" 
						:key="indexItem" 
						@click="toQuestion(overGroupVo.minNum + indexItem - 1)" 
						:class="['stgl-whsq-group-link', 
							{'error': formList[overGroupVo.minNum+indexItem-2] && formList[overGroupVo.minNum+indexItem-2].validError},
							{'group-active': formList[overGroupVo.minNum+indexItem-2] && formList[overGroupVo.minNum+indexItem-2]._id==isFocus}
							]">{{overGroupVo.minNum + indexItem - 1}}</a>
				</div>
			</template>
			<template v-else>
				<div v-for="item of allGroupData" 
					:key="item" 
					@click="toQuestion(item)" 
					:class="['stgl-whsq-group-num', 
							{'error': formList[item-1] && formList[item-1].validError},
							{'group-active': formList[item-1] && formList[item-1]._id==isFocus}]">
							<span>{{item}}</span>
				</div>
			</template>
			<div class="stgl-whsq-group-num top" @mouseover="overGroupVo=null" @click="toQuestion(1)"><img src="./static/img/question/whsq-top-icon.png" /></div>
		</div>
	</div>
</template>
<style type="text/css">
	.ag-paragraph{list-style: inherit;}
	#contenteditble:focus{outline: none;}
	.contentedit{padding: 5px;height: 500px;background-color: #fff;overflow: auto;}
	.item-img:hover .insert-img-zhezhao{margin-top: 0;top: 70%;transition: top .2s ease-in-out;}
	.item-img{position: relative;overflow: hidden;    margin: 10px 0;}
	.insert-img-zhezhao .remove-file{color: #fff;display: block;line-height: 1rem;}
	.insert-img-zhezhao{right: 0;margin: 0;margin-top: 1px;cursor: pointer;position: absolute;width: 100%;height: 30%;background: rgba(0, 0, 0, 0.34);z-index: 1;top:100%;transition: top .2s ease-in-out;text-align: center;display: flex;display: -ms-flexbox;justify-content: center;align-items: center;}
</style>
<script>
	
	//加载远程翻译代码
	app.loadCode(['SYSCODE_exam029', 'SYSCODE_exam011','SYSCODE_exam032','SYSCODE_exam025']);
	//选择题答案列表
	const BASE_URL = '/admin/core/examcorequestion/'
	const ZSD_TREE_NODE = '/admin/core/examcorecategory/zsdList'
	const KCDG_TREE_NODE = '/admin/core/examcorecategory/kcdgList'
	const QUERY_USER_ALL = 'admin/ext/examextprivatecode/queryAuthor'
	import {BAR_ARR} from 'bar_arr.js';
	export default {
		mixins: [BASE_ADD], // 对象混入
		components: {
			'paper': app.vload(`admin/core/examcorequestion/components/paper.html`),
			'compound': app.vload(`admin/core/examcorequestion/components/compound.html`),
			'img-material': app.vload(`admin/core/examcorequestion/components/img-material.html`),
			'my-upload': app.vload(`admin/core/examcorequestion/components/img-upload.html`)
		},
		data() {
			return {
				mode: 'normal', // 模式（常规模式-normal；md模式-md）
				mdText:'',//md编辑器中md文本内容
				screenNum: null, // 视口可显示的最大试题序号分组数
				allGroupData: null, // 试题分组数据
				overGroupVo: null, // 当前经过的试题序号分组对象
				viewPictureShow:false,//图片查看
				viewPictureUrl:'',//正在展示的图片
				theme2: 'light',
				barArr:BAR_ARR,
				baseUrl: BASE_URL,
				saveUrl: 'saveQuestionsBatch',
				queryAuthorUrl:QUERY_USER_ALL,
				vo: {
					id:'',
					flag: '0',//试题类型
					difficulty: '',//试题难度
					author: '', // 出题人
					score:'', // 分值占比
					flagAlias:'', // 别名
					outlineIds: [], // 试题大纲
					knowledgeId: '', // 知识点
					// 编程题字段
					codeQueInfo:{
						question: '', // 编程题 - 试题描述
						main: '', // 编程题 - 启动方法
						a: '', // 编程题 - 参考答案
						aTemplate: '', // 编程题 - 答题模板
						inOut: [], // 编程题 - inOut
						compile: '', // 编程题 - 控制台
					},
					codeLanguage: '', // 编程题 - 试题语言
					// 翻译题字段
					titleArr: [{title: '', answer: '', remark: ''}], // 翻译题 - 试题内容
					title: '', // 题干富文本
					option1:'',//选项A
					option2:'',//选项B
					option3:'',//选项C
					option4:'',//选项D
					option5:'',//选项E
					option6:'',//选项F
					answer: '', //正确答案
					richText: '1',
					remark:'',//试题解析
					remarkMode:'0',
					fileList:[],
					nodes:null,
					columnNum: 2, // 翻译题-一行几列
				}, 
				expression: {src: '', mathTxt:'',arrayMap:[]}, // 题干公式对象
				questionId: '',
				formList:[],//多试题创建验证对象
				mdShow:false,
				fieldList:[
					{title:"题型分类",field:"flag"},
					{title:"题型",field:"flagAlias"},
					{title:"难易度",field:"difficulty"},
					{title:"分值占比",field:"score"},
					{title:"出题人",field:"author"},
					{title:"课程大纲",field:"outlineIds"},
					{title:"知识点",field:"knowledgeId"},
				],
				optionList:[
					{key:'选项A',title:"",field:"option1"},
					{key:'选项B',title:"",field:"option2"},
					{key:'选项C',title:"",field:"option3"},
					{key:'选项D',title:"",field:"option4"},
					{key:'选项E',title:"",field:"option5"},
					{key:'选项F',title:"",field:"option6"},
				],
				fieldAnswer:{title:"正确答案",field:"answer",type:'ZQDA'},
				fieldTitle:{title:"题干",field:"title",type:'TG'},
				fieldRemark:{title:"试题解析",field:"remark",type:'JX'},
				templateImgList:[
					/* {src:'/static/img/question/template1.png',questionList:['5','5','5']},
					{src:'/static/img/question/template2.png',questionList:['2','5','1']}, */
					{src:'./static/img/question/template3.png',questionList:[]}
				],
				srcActive:'',
				compoundCurrent:'',
				compoundMap:{},
				isFocus:'',  //当前一级题标记(_id)
				editorRangeVo: { // 记录编辑器失去焦点时的光标位置，用于插入公式时使用
					index: null, // 光标位置
					id: '', // 当前编辑器ID (当页面中多个编辑器时，用于区分插入到哪个编辑器)
				},
				userAllMap: {}, // 出题人所有数据
				userMap: {} ,// 出题人列表
				flagMap:{},//题型列表
				defaultCreateUser:'',//默认出题人(这里是id)
				defaultConfigVo: { // 默认配置对象
					outlineIds: [], // 试题大纲
					knowledgeId: '', // 知识点
					difficulty: '',//试题难度
					author: '', // 出题人
				},
				copying:true,//正在复制(防止多次复制)
				fileMap:{}, //记录所有图片信息
				importConfig: { // 上传素材配置对象
					showUploadList: true,//是否显示上传列表
					format: ['zip'], //文件类型
					name: 'file',//文件名字
					data: {},//上传时附带参数
					headers: { token: app.getUser() && app.getUser().token },//设置请求头
					type: 'drag',
					action: app.API_URL + '/admin/core/examcorequestion/importMd',//素材上传地址，这里可能要改
				},
			}
		},
		provide() {
			return { // 需要插入公式的编辑器数据对象
				editorRangeVo: this.editorRangeVo,
				changeEditorRangeData: this.changeEditorRangeData,
				fileMap:this.fileMap
			}
		},
		beforeRouteLeave(to, from, next){
			app.getUtil().scrollPublic(null,this.scroll)//移除滚动事件
			document.querySelectorAll('.v-transfer-dom').forEach(e=>{
				e.remove()
			})
			next()
		},
		mounted: function () {
			const that = this
			document.oncopy = function(e){
				//如果
				if(window.getSelection().getRangeAt(0).collapsed){
					app.copyText('#copyQuestion!');
					//记录复制对象key
					that.copyFocus = that.isFocus

				}else{
					that.copyFocus = ''
				}
			}
			document.onpaste = function(e){
				if(e.clipboardData.getData('text')=="#copyQuestion!" && that.copying){
					that.onkeydown({altKey:true})
					e.preventDefault()
				}
			}
			document.onkeydown = function(e){
				if(e.key=='Enter' && e.ctrlKey && that.copying){
					that.onkeydown(e,true)
				}
			}

			// 获取所有出题人信息后 (设置默认配置)
			this.getAllUser(() => this.globalConfig());

			//var linkTag = document.createElement("link");
			//linkTag.rel = "stylesheet";
			//linkTag.type = "text/css";
			//linkTag.href = '/static/katex/muya.css'
			//linkTag.setAttribute('id','muya')
			//document.head.appendChild(linkTag);
			//
			//var script = document.createElement('script');
			//script.charset = 'utf-8';
			//script.timeout = 120;
			//script.src = "/static/katex/muya.min.js";
			//document.head.appendChild(script);
			this.getTreeNode()
			this.elFixed();
			// 1、获取可显示的试题序号分组的最大数 （屏幕可用高度-上下两边间距-“回到顶部”内容高度）/ 每个组的高度
			this.screenNum = Math.floor((document.body.clientHeight - 400 - 65) / 83);
		},
		methods: {
			computedQuestionGroup() { // 计算试题分组
				if (this.screenNum < 0) return;
				// 2、试题总数
				const allQuestionNum = this.formList.length;
				// 3、判断试题总数和总组数的关系：
				if (allQuestionNum <= this.screenNum) {
					// 3-1、试题总数 <= 总组数：直接罗列; allGroupData = 试题总数； 
					this.allGroupData = allQuestionNum;
				} else {
					// 3-2、试题总数 > 总组数，拆分试题为含有minNum和maxNum的对象数组；allGroupData = [{ minNum: 1, maxNum: 10 }, { minNum: 11, maxNum: 20 }];
					this.allGroupData = [];
					const spaceNum = Math.ceil(allQuestionNum / this.screenNum), // 每组几位
					groupNum = Math.ceil(allQuestionNum / spaceNum); // 防止 < 最大组数
					for (let i = 0; i < groupNum; i++) {
						// 记录最小试题序号、最大试题序号、区间的总错题数
						const minNum = i * spaceNum + 1,
						maxNum = (i + 1) * spaceNum > allQuestionNum ? allQuestionNum : (i + 1) * spaceNum,
						questionArr = this.formList.slice(minNum, maxNum+1);
						let validErrorNum = questionArr.filter(questionItem => questionItem.validError).length;
						// 更新数组
						this.allGroupData.push({ minNum: minNum, maxNum: maxNum, validErrorNum: validErrorNum });
					}
				}
				console.log(this.allGroupData)
			},
			toQuestion(questionIndex) { // 跳转到具体试题
				const questionData = this.formList[questionIndex-1],
				questionElem = document.getElementById(`question-item-${questionData._id}`);
				questionElem && questionElem.scrollIntoView();
				this.isFocus = questionData._id
			},
			questionTypeVo(flag) { // 试题类型对象
				const vo = {};
				this.flagMap[flag].forEach((e) => {
					e.flag && (vo[e.flagAlias] = e);
				});
				//console.log(vo);
				return vo;
			},
			removeFile(row,index,list){
				app.confirm({
				    title: '提示',
				    content: '确定移除此图片',
					type: 'warning',
				    cancelText: '取消',
					okText: '确定',
				    onOk: function () {
						list.splice(index,1)
					},// 点击确认按钮的回调
				    onCancel: function () {} // 点击取消按钮的回调
				});
			},
			menuTitleClick(e,type){
				const list = this.flagMap[e.param].filter((e)=>{return e.flag})
				if(list.length==1){
					this.menuClick(type,e,list[0].flagAlias)
				}
			},
			elFixed(){
				this.top = this.$refs['stgl-bar'].getBoundingClientRect().top
				app.getUtil().scrollPublic(this.scroll)
			},
			scroll(e){
				if(this.$refs['stgl-bar']){
					if (this.formList.length > 0) {
						if(!e.target.clientRectTop && e.target.clientRectTop!==0){
							e.target.clientRectTop = this.top
						}
						if(e.target.scrollTop >= e.target.clientRectTop - 80){
							this.$refs['stgl-bar'].classList.add('stgl-bar-fixed')
						}else{
							this.$refs['stgl-bar'].classList.remove('stgl-bar-fixed')
						}
					} else {
						this.$refs['stgl-bar'].classList.remove('stgl-bar-fixed')
					}
				}
			},
			
			/* 点击的单个表单 */
			formClick(item){
				this.isFocus = item._id
			},
			/* 获取树结构 */
			getTreeNode(){
				/* 知识点 */
				this.$http.post(ZSD_TREE_NODE,{enable:"1"}).then((res)=>{
					window.sessionStorage.setItem('knowledgeIdNode',JSON.stringify(res.data))
				})
				/* 课程大纲 */
				this.$http.post(KCDG_TREE_NODE,{enable:"1"}).then((res)=>{
					window.sessionStorage.setItem('outlineIdsNode',JSON.stringify(res.data))
				})
				/* 试题分类 */
				this.$http.post('/ext/examextflagalias/flagAliasList',{enable:"1"}).then((res)=>{
					const map = {}
					const map2 = {}
					this.flagAliasMap = {}
					if(res.data){
						for(const o of res.data){
							if(!o.flag){
								map[o.flagAlias] = [o];
								map2[o.flagAlias] = o;
							}
							this.flagAliasMap[o.flagAlias] = o.name
						}
						for(const o of res.data){
							if(o.flag){
								try{
									if(map2[o.flag]){
										this.$set(o,'pId',map2[o.flag]['id'])
									}
									map[o.flag].push(o)
								}catch(e){
									console.log(e)
									//TODO handle the exception
								}
							
							}
						}
						this.flagMap = map
					}
						
				})
			},
			changeEditorRangeData(data) { // 后代组件修改editorRangeVo的值
				Object.assign(this.editorRangeVo, data);
			},
			/* 
			 * 返回复制的内容 
			 * @param empty 是否复制空内容
			 * @param dv 是否使用全局配置
			*/
			returnCopyVo(empty,dv){
				let vo = {}
				//1.判断题型（如果题型是复合题）
				const id = this.copyFocus?this.copyFocus:this.isFocus;
				let e = this.compoundMap[id]
				if(e){
					vo = e
				}else{
				//2.判断题型（如果是普通题型）
					let cp = this.formList.find(item=>{return  item._id == id})
					vo = cp || this.formList[this.formList.length-1]
				}
				
				const obj = vo ? this.copyVo(vo) : this.copyVo(this.vo);
				if(!empty){ //如果按alt键(即赋值空内容试题)
					if(obj.nodes){
						obj.nodes.forEach(item=>{
							const cpVo = this.copyVo(this.vo)
							delete cpVo['flagAlias'];
							delete cpVo['flag'];
							Object.assign(item,cpVo)
						})
					}
					const cpVo = this.copyVo(this.vo)
					delete cpVo['flagAlias'];
					delete cpVo['flag'];
					delete cpVo['nodes'];
					Object.assign(obj,cpVo)
				}
				if(dv){
					//关闭读取全局配置
					//Object.assign(obj,this.copyVo(this.defaultConfigVo))
				}
				return obj
			},
			/* ctrl+Enter(复制空试题模板)/ctrl+alt+Enter(复制试题--复制所有属性) 添加试题 */
			onkeydown(e,dv){
				//1.判断复制内容
				const cp = this.returnCopyVo(e.altKey,dv)
				this.validateAll().then(()=>{
					this.copying = false;//正在复制 
					app.loading.show(); 
					const id = this.copyFocus?this.copyFocus:this.isFocus;
					let m = this.compoundMap[id]
					if(m){
						//添加一个复合题
						this.addTest(cp.flag,'2',cp)
					}else{
						//添加一个普通题
						this.addTest(cp.flag,'1',cp)
					}
					const timer = setTimeout(()=>{
						this.copying = true;//复制 完成
						if(timer){
							clearTimeout(timer)
						}
					},3000)
					app.message({
						type: 'success', 
						message: "复制成功",
						duration:1500,
					});
				}).catch((e)=>{
					app.message({
						type: 'warning', 
						message: e,
						duration:4000,
					});
					//this.copying = true;//复制 完成
					// 隐藏正在添加试题效果
					app.loading.hide();
				})
			},
			copyEmit(){
				this.onkeydown({altKey:true})
			},
			getAllUser(cb) { // 获取所有出题人列表
				const user = app.getUser();
				//this.defaultCreateUser = user.id
				this.defaultCreateName = user.userNameCn
				//this.userMap[user.id] = user.userNameCn
				this.vo.author = user.id //设置默认出题人
				this.$http.post(this.queryAuthorUrl, {pageNum:1,pageSize:10}).then((res) => {
					this.userMap = {[user.id]:user.userNameCn}
					this.userAllMap = {[user.id]:user}
					res.data.forEach(e=>{
						this.$set(this.userMap,e.userId,e.name)
						this.$set(this.userAllMap,e.userId,e)
					})
					cb && cb();
				})
				
			},
			manageUser() { // 管理出题人
				const _this = this;
				app.dialog.show(app.getRouteLinkpre() + 'admin/ext/examextprivatecode/add-user.html', {
					data(){
						return{
							updateUserMap:_this.userMap
						}
					},
					mounted(){
						this.currentId =  _this.defaultConfigVo.author || this.user.id;
						// this.currentName = _this.defaultCreateName
					},
					watch:{
						// currentId(v){
						// 	_this.vo.author = v;
						// 	const e = this.userList.find(e=>{return e.userId==v})
						// 	if(e){
						// 		_this.defaultCreateName = e.name
						// 	}
						// }
					},
					methods: {
						updateQuestionUser() { // 更新试题的ID
							this.$nextTick(()=>{
								// 如果试题出题人刚好为删除的出题人时，需要重新赋值为登录用户
								_this.formList.forEach(item => {
									!item.author && (item.author = this.user.id);
								});
							});
						}
					}
				})
			},
			/* 根据模板添加试题 */
			addQuestionFromTem(item={}){
				//1.添加一个复合题主题干
				this.validateAll().then(()=>{
					this.addTest('9','2')
				}).catch((e)=>{
					app.message({
						type: 'warning', 
						message: e,
						duration:4000,
					});
					// 隐藏正在添加试题效果
					app.loading.hide();
				})
				//2.添加复合题子题
				//item.questionList.forEach(flag=>{this.addTest(flag,'2')})
			},
			menuClick(key,e,flagAlias){

				/* 如果是添加试题 */
				if(key==='1' || key==='2'){
					if(this.mode=='md'){

						this.$Message.warning('md模式下不能通过此方式添加，请切换试题模式');
						return
					}
					if (key == '2') {
						// 判断当前选中的是否为复合题
						if (!this.isFocus) {
							this.$Message.warning('请先添加复合题模板,或选择复合题焦点');
							return;
						}
						const currentFocusData = this.formList.filter(formItem => formItem._id == this.isFocus);
						if (currentFocusData[0] && currentFocusData[0].flag != '9') {
							this.$Message.warning('请先添加或点击需要添加的复合题');
							return;
						}
					}


					// 显示正在添加试题效果
					app.loading.show();
					// 验证
					this.validateAll().then(()=>{
						this.addTest(e.param,key,null,flagAlias)

					}).catch((e)=>{

						app.message({
							type: 'warning', 
							message: e,
							duration:4000,
						});
						// 隐藏正在添加试题效果
						app.loading.hide();


					})
				}else if(key==='4'){//如果是添加公式
					this.math()
				}
			},
			/* 删除本题 */
			deleteEmit(item){
				this.deleteTest(item)
			},
			clickMath(v){
				this.math(v)
			},
			math(editPanel=""){
				let that = this;
				app.dialog.show(app.getRouteLinkpre()+'admin/core/examcorequestion/components/model-math.html',
				    {
				        data(){ return {
							defaultEditPanel:editPanel
						}},
				        methods:{
							saveCallback(mathTxt,isEdit) {
								if (that.editorRangeVo.id) {
									// 记录公式对象
									Object.assign(that.expression, {
										// src: src,
										mathTxt: mathTxt,
										isEdit: this.isEdit
									});
								} else {
									that.$Message.warning('请点击光标位置');
								}
								// 关闭模态框
								this.addModelShow = false;
							}
						},
				    }
				);
				
			},
			htmlToMark(h){
				const trunServer = new TurndownService()
				trunServer.use(turndownPluginGfm.gfm)
				return trunServer.turndown(h)
			},
			markToHtml(m){
				return marked.parse(m)
			},
			formatFiledList(e,type){
				let str = '';
				//2.解析试题答案  (////)
				let answer = '';
				let remark = '';
				let title  = '';
				const obj = JSON.parse(JSON.stringify(e))
				Object.assign(obj,this.filterKatex(obj))
				//如果是判断题
				if(e.flag=='0'){
					answer = e.answer=='A'?'正确':'错误';
				}
				//如果是翻译题
				if(e.flag=="8"){
					this.formatTranslate(obj,true)
					obj.title = obj.title.replaceAll('\n','\\n')
					obj.remark = obj.remark.replaceAll('\n','\\n')
					obj.answer = obj.answer.replaceAll('\n','\\n')
				}
				
				
				//4.解析试题题干 (###)----\xa0空格
				const titleSign = type=='child'?'####\xa0':'### \xa0';
				
				//const t = this.htmlToMark(e[this.fieldTitle.field])
				
				str +=  `\n${titleSign}${obj.title}\n`;


				//如果是复合题子题
				let field = '/// '
				if(type=='child'){
					field += `flag:${obj.flag},flagAlias:${obj.flagAlias}\n`
					str += field
				}

				//5.解析数学公式($$---$$ 	)
				console.log(obj.title)
				//解析试题答案(////)
				str +=  `//// ${obj.answer}\n`;
				
				//3.解析试题解析 (/////)
				str +=  `///// ${obj.remark}\n`;
				
				
				//7.选项（如果是选择题）
				if(obj.flag=='1' || obj.flag=='2'){
					for(const option of this.optionList){
						//if(e[option.field]){
							str += `-  ${obj[option.field]}\n`
						//}
					}
				}
				return str;
			},
				
			/* 复合题 4*#答案 ////题干 ### */
			mdShowM1(type){
				this.mode = type;//切换模式
				//如果是关闭状态
				if(type=='md'){
					let strAll = ''
					this.formList.forEach(e=>{
						let str = '/// '//后面解析需要的html格式的串
						//1.解析试题属性
						this.fieldList.forEach(k=>{
							//1.2翻译属性，找出属性对应翻译
							let value = '',key ='';

							value = e[k.field];
							key = k.field;
							if(k.field=='difficulty'){
								key = 'star'
							}
							str += `${key}:${value},`
						})
						if(e.nodes){
							str  += `\n### \xa0${e.title}\n`;

							for(const el of e.nodes){
								str  += this.formatFiledList(el,'child')
							}
						}else{
							str  += this.formatFiledList(e)
						}
						//// 正确答案: sdfas\n///// 试题解析:fdfsd\n- 选项A:fdasf- 选项B:dsfsdf
						
						//6.试题分割线
						str += `---\n`
						strAll += str
						  
					})
					strAll = strAll.replaceAll('<p><br></p><p><br></p>','\n')
					strAll = strAll.replaceAll('<p><br></p>','\n')
					strAll = strAll.replaceAll('<p>','')
					strAll = strAll.replaceAll('</p>','\n')
					strAll = strAll.replace(/\\begin\{equation\}/g,'')
					strAll = strAll.replace(/\\end\{equation\}/g,'')
					console.log(strAll)
					window.Muya.setMarkdown(strAll)
					//str = '<table><tr><th align="right">1</th><th>1</th><th>1</th></tr><tbody><tr><td>5</td><td>5</td><td>5</td></tr></tbody></table>'
					//const t = this.htmlToMark(str)
					//let c = marked.parse("| 1 | 1 | 1 |\n| --: | --- | --- |\n| 5 | 5 | 5 |")
					
				}else{
					//还原所有数据----
					this.formList.splice(0)
					//(单/多选)记录所有选项字段
						let optionMap = {}
						//this.optionList.forEach(e=>optionMap[e.field] = {title:'',field:e.field})
						//记录一下所有字段名称
						const map = new Map()
						this.fieldList.forEach(e=>map.set(e.title,e.field))
						//获取md文本
						const text = document.getElementById("source").value
						const questionList = text.split('---')
						
						//分题
						questionList.forEach((row,index)=>{
							if(row.trim()==""){return}
							const textList = row.split('\n')
							//设置标题标记
							let isTitle = false
							const tO = {title:'',field:'title'},
								  aO = {title:'',field:'answer'},
								  rO = {title:'',field:'remark'}
							let signO = {}
							for(const textRow of textList){
								if(textRow.trim().startsWith('///')){
									const str = textRow.substring(4,textRow.length)
									const list = str.trim().split(',')
									let m = {}
									list.forEach(e=>{
										const arr = e.split(':')
										if(arr[0]=='star'){arr[0] = 'difficulty'}
										m[arr[0]] = arr[1] || ''
										//如果是多选
										if(arr[0]=='outlineIds'){
											if(/^\[.*\]$/.test(arr[1])){
												const arrO = arr[1].substring(1,arr[1].length-1).split(',')
												//这里需要将number类型转换string类型
												m[arr[0]] = arrO.map(el => el += '')
											}else{
												m[arr[0]] = [arr[1]]
											}
										}
									})
									if(!this.formList[index]){
										const cp = this.copyVo(this.vo)
										cp._id = '_'+parseInt(Math.random()*100000000)
										if(m.flag=="9"){
											this.compoundMap[cp._id] = cp
											m.nodes = []
										}
										this.formList.push(Object.assign(cp,m))
									}
									break
								}
							}
							const flag = this.formList[index]['flag']
							//如果是一道复合题
							const infoMap = {}
							
							textList.forEach((e,i)=>{
								console.log('----',e)
								/* if(e.trim()===''){return} */
								let str = e.replace(/^\s+/,'') //去掉开头空格
								/* 特殊符号集合(记录所有特殊符号的集合) */
								const specialSignList = ['///// ','//// ','/// ','### ','#### ','- ','---']
								//2.还原(题干，解析，答案...等)内容
								if(str.startsWith('### ')){//如果是试题题干、
									tO.title = "<p>"+e.substring(3)+"</p>";
									signO = tO;
								}else if(str.startsWith('//// ')){//如果是试题答案

									//如果开始录入子题
									if(infoMap.currentK){
										signO = infoMap[infoMap.currentK].answer;
										signO.title = str.substring(4).trim();
									} else {
										if(['0','1','2'].includes(flag)){
											aO.title = str.substring(4).trim();
										}else if(['3','4','5','8'].includes(flag)){
											aO.title = "<p>"+str.substring(4).trim()+"</p>";
										}
										signO = aO;
									}
								}else if(str.startsWith('///// ')){//如果是试题解析
									//如果开始录入子题
									if(infoMap.currentK){
										signO = infoMap[infoMap.currentK].remark;
										signO.title = "<p>"+str.substring(5).trim()+"</p>";
									} else {
										rO.title = "<p>"+str.substring(5).trim()+"</p>";
										signO = rO;
									}
								}else if(str.startsWith('#### ')){//如果是复合题子题题干
									if(flag=='9'){
										const voItem = this.copyVo(this.vo)
										voItem._id = '_'+parseInt(Math.random()*100000000)
										voItem._pId = this.formList[index]['_id']
										this.formList[index].nodes.push(voItem)
										this.compoundMap[_id] = voItem
										infoMap.currentK = voItem._id
										infoMap[voItem._id] = {
											title:{title:"<p>"+str.substring(4).trim()+"</p>"},
											answer:{title:""},
											remark:{title:""},
											vo:voItem
										}
										signO = infoMap[voItem._id].title
									}
								}else if(str.startsWith('/// ')){
									if(infoMap.currentK){
										const list = str.substring(4).trim().split(',')
										let m = {}
										list.forEach(e=>{
											const arr = e.split(':')
											if(['flag','score','flagAlias'].includes(arr[0])){
												m[arr[0]] = arr[1] || ''
											}

										})
										Object.assign(infoMap[infoMap.currentK].vo,m)
									}
								}


								//3.还原(单/多选)选项内容
								if(this.formList[index]['flag']=='1'||this.formList[index]['flag']=='2'){
									if(str.startsWith('- ')){
										if(!optionMap.current){
											optionMap['option1'] = {title:''}
											optionMap.current = 1
										}else{
											optionMap.current++
											if(optionMap.current<='6'){
											optionMap[`option${optionMap.current}`] = {title:''}
											}
										}
										if(optionMap[`option${optionMap.current}`]){

											signO = optionMap[`option${optionMap.current}`]
											signO.title = "<p>"+str.substring(1).trim()+"</p>"
										}

									}
								}
								//复合题还原选项
								if(flag=='9'){
									if(str.startsWith('- ')){
										const key = infoMap.currentK
										if(key){
											if(!infoMap[key]['optionMap']){
												infoMap[key]['optionMap'] = {}
												infoMap[key]['optionMap']['option1'] = {title:''}
												infoMap[key]['optionMap'].current = 1
											}else{
												infoMap[key]['optionMap'].current++
												if(infoMap[key]['optionMap'].current<='6'){
													infoMap[key]['optionMap'][`option${infoMap[key]['optionMap'].current}`] = {title:''}
												}
											}
											signO = infoMap[key]['optionMap'][`option${infoMap[key]['optionMap'].current}`]
											signO.title = "<p>"+str.substring(1).trim()+"</p>"
										}
									}
								}
								//判断是否遇到特殊字符
								let specialFlagFalse = true
								for(const c of specialSignList){
									if(str.startsWith(c)){
										specialFlagFalse = false
										break
									}
								}
								//如果没遇到特殊字符
								if(specialFlagFalse){
									signO.title += "<p>"+e+"</p>"
								}
								
							})
							this.formList[index][this.fieldTitle.field] = tO.title
							this.formList[index][this.fieldAnswer.field] = aO.title
							this.formList[index][this.fieldRemark.field] = rO.title

							//处理复合题子题选项
							const nodes = this.formList[index].nodes;
							delete infoMap.currentK
							for(const k in infoMap){
								infoMap[k].vo.title = infoMap[k]['title']['title']
								infoMap[k].vo.answer = infoMap[k]['answer']['title']
								infoMap[k].vo.remark = infoMap[k]['remark']['title']
								if(infoMap[k].optionMap){
									delete infoMap[k].optionMap['current']
									for(const mk in infoMap[k].optionMap){
										infoMap[k].vo[mk] = infoMap[k].optionMap[mk]['title']
									}
								}

							}



							console.log(infoMap)
							delete optionMap.current

							//如果是翻译题
							if(flag=="8"){
								this.formList[index]['titleArr'] = this.translateMDToForm(this.formList[index])
							}

							for(const k in optionMap){
								this.formList[index][k] = optionMap[k].title
							}
							//this.formList[index][this.fieldTitle.field] = tO.title
							optionMap = {}//清空记录
						})
				}
				this.isFocus = this.formList[0] && this.formList[0]['_id']
				this.mdShow=!this.mdShow
			},
			/* 
			  * 翻译题转form表单
			  * */
			translateMDToForm(vo){
				let arr = []
				let title = vo.title.replaceAll('<p>','').replaceAll('</p>','')
				let answer = vo.answer.replaceAll('<p>','').replaceAll('</p>','')
				let remark = vo.remark.replaceAll('<p>','').replaceAll('</p>','')
				const arrT =  title.split('\\n')
				const arrA =  answer.split('\\n')
				const arrR =  remark.split('\\n')


				arrT.forEach((e,i)=>{
					const column = e.split('|||')
					const len = column.length
					let columnA = []
					let columnR = []
					if(arrA[i]){
						columnA = arrA[i].split('|||')
					}
					if(arrR[i]){
						columnR = arrR[i].split('|||')
					}
					column.forEach((item,j)=>{
						if(item){
							arr.push({remark:columnR[j],title:item,answer:columnA[j],index:arr.length,column:len})
						}

					})
				})
				return arr;
			},
			/*
			 * @param k(试题类型,默认判断题)
			 * @param type(左侧menu操作类型，1(普通题添加)2(复合题添加))
			 * @param targetVo (要拷贝的对象--可传可不传)
			 * @param flagAlias(添加的当前题的类型)
			 */
			addTest(k='0',type,targetVo,flagAlias){
				//1.拷贝
				const cp = targetVo || this.copyVo(this.vo) //原始对象
				cp.validError = false; // 初始化验证失败状态
				//2.设置试题类型
				cp.flag = k;
				cp.id = '';
				if(flagAlias){
					cp.flagAlias =  flagAlias
				}
				//3.设置具体题型flagAliasName
				const el = this.flagMap[k].find((e)=>{return e.flagAlias == cp.flagAlias})
				cp.flagAliasName = el ?  el.name : '';
				//4.设置_id
				cp._id = '_'+parseInt(Math.random()*100000000)
				
				//5.执行添加操作
				
				//5.1--如果是新增复合题('9')
				if(type === '2'){
					//如果焦点在复合题中
					if(k!='9'){//如果是复合题子题
						let e = this.compoundMap[this.isFocus]//找到当前复合题焦点(可能是子题可能是父级)
						if(e){
							//1.如果焦点在复合题子题中(即有父级节点)
							if(e._pId){
								cp._pId = e._pId;
								const p = this.compoundMap[e._pId]	
								if(p && p.nodes){
									let index = p.nodes.findIndex(item=>{return  item._id == p.compoundItemActive})
									p.nodes.splice(index+1,0,cp)
								}
							}else{//2.如果焦点在复合题父级节点(即节点没有父级)
								cp._pId = e._id;//设置子题的pId
								if(e.nodes){
									this.$set(e.nodes,e.nodes.length,cp)
								}
							}
							
						}else{
							//创建一道复合题主体()
							const cpChild = Object.assign({},cp)
							cpChild._id = '_'+parseInt(Math.random()*100000000)
							cpChild._pId = cp._id
							this.compoundMap[cpChild._id] = cpChild;//录入子题
							this.compoundMap[cp._id] = cp//录入复合题主题
							cp.flag = '9'
							for(const e of this.flagMap['9']){
								if(e.name && e.flagAlias!='9'){
									cp.flagAlias = e.flagAlias
									cp.flagAliasName = e.name
									break
								}       
							}
							if(!cp.nodes){this.$set(cp,'nodes',[])}
							this.$set(cp.nodes,cp.nodes.length,cpChild)
							this.$set(this.formList,this.formList.length,cp)
							this.isFocus = cp._id
						}
					}else{
						cp.nodes = cp.nodes || []
						const index = this.formList.findIndex(item=>{return this.isFocus === item._id});
						this.isFocus = cp._id
						for(const e of this.flagMap['9']){
							if(e.name && e.flagAlias!='9'){
								cp.flagAlias = cp.flagAlias?cp.flagAlias:e.flagAlias
								cp.flagAliasName = cp.flagAliasName?cp.flagAliasName:e.name
								break
							}       
						}
						for(const el of cp.nodes){
							el._id = '_'+parseInt(Math.random()*100000000)
							el._pId = cp._id
							this.compoundMap[el._id] = el
						}
						if(index!=-1){
							this.formList.splice(index+1,0,cp)
						}else{
							this.formList.push(cp)
						}
					}
					this.compoundMap[cp._id] = cp 
				}else{
					//5.2--如果是新增普通题型
					const index = this.formList.findIndex(item=>{return this.isFocus === item._id});
					if(index!=-1){
						this.formList.splice(index+1,0,cp)
					}else{
						this.formList.push(cp)
					}
					this.isFocus = cp._id
				}
				// 隐藏正在添加试题效果
				const loadingVo = setTimeout(() => {
					this.computedQuestionGroup(); // 重新计算试题分组信息
					app.loading.hide(); 
					clearTimeout(loadingVo);
				}, 500);
			},
			deleteTest(e){
				if(e){
					//1.如果是删除复合题整题，先删除子题map
					const pNode = this.compoundMap[e._id]
					if(pNode && pNode.nodes){
						for(const o of pNode.nodes){
							if(o._pId==e._id){
								this.$delete(this.compoundMap,o._id) 
							}
						}
						this.$delete(this.compoundMap,e._id)
					}
					//2.删除整题
					const index = this.formList.findIndex(item=>{return e._id === item._id});
					this.$delete(this.formList,index)
					
					//3.重新赋值焦点id
					 if(this.formList[index]){
						this.isFocus = this.formList[index]['_id']
					}else if(this.formList[index-1]){  
					
						this.isFocus = this.formList[index-1]['_id']
					}else{
						this.isFocus = ''
					}

					// 4.重新计算试题分组信息
					this.computedQuestionGroup();
					
				}else{
					this.formList = []
					this.compoundMap = {}
				}
			},
			uploadImg(item){
				let that = this;
				app.dialog.show(app.getRouteLinkpre()+'admin/core/examcorequestion/components/upload-img.html',
				    {
				        data(){ return {}},
				        methods:{},
				        watch:{
				            importList(v){
								v.forEach(e=>{
									item.children.unshift({title:'',id:'',param:e.url})
									that.fileMap[e.fingerprint] = e
								})
				            }
				        }
				    }
				);
			},
			validateAll(){
				const  arr = []
				this.formList.forEach((e,i)=>{
					const k = 'form'+i
					arr.push(this.$refs[k][0].validate())
				})
				return  new Promise((resolve, reject) => {
						Promise.allSettled(arr).then(e => {
							// 初始化所有试题为验证通过状态
							this.formList.forEach(formItem => this.$set(formItem, 'validError', false));
							Array.isArray(this.allGroupData) && this.allGroupData.forEach(groupItem => groupItem.validErrorNum = 0);
							// 过滤出验证失败的对象
							const rejectData = e.filter(eItem => eItem.status == 'rejected');
							if (rejectData.length == 0) { // 验证通过
								resolve();
							} else {
								rejectData.forEach(rejectItem => { // 验证未通过
									// this.formList[rejectItem.reason-1].validError =  true;
									this.$set(this.formList[rejectItem.reason - 1], 'validError', true);
								});
								if (Array.isArray(this.allGroupData)) { // 区间形式
									this.allGroupData.forEach(groupItem=>{
										// 记录最小试题序号、最大试题序号、区间的总错题数
										const questionArr = this.formList.slice(groupItem.minNum-1, groupItem.maxNum);
										groupItem.validErrorNum += questionArr.filter(questionItem => questionItem.validError).length;
									});
								}

								reject('存在未完成的试题！');
							}
						})
				})  
			},
			download() { // 打包下载
				if (this.formList.length == 0) {
					app.message({
						type: 'warning',
						message: '不能直接打包下载，请添加试题!',
						duration: 4000,
					});
					return;
				}
				this.validateAll().then(() => {
					//如果有复合题子题总分数占比不足100
					if(this.scoreCount()){
						return
					}
					const objArr = this.beforeSave(false);
					// 后台验证
					this.$http.post('/admin/core/examcorequestion/checkOnlineQuestions', objArr).then(res => {
						if (res.data.success) {
							// 后台下载
							const fileName = this.$moment(new Date()).format("YYYYMMDDHHmmss");
							window.open(`${app.API_URL}/app/download?fileId=${res.data.data}&downloadName=试题-${fileName}.zip`);
						} else {
							// 提示信息
							app.confirm({ // 点击确认按钮的回调
							    title: '提示',
							    content: `${res.data.message}`,
								type: 'warning',
								isShowCancelBtn: false,
								okText: '确定',
							    onOk: function () {},
							    onCancel: function () {} // 点击取消按钮的回调
							});
							// 跳转到第一个错误试题
							 if(res.data.numList){
								this.toQuestion(res.data.numList[0]);
								res.data.numList.forEach(i=>{
									this.$set(this.formList[i-1], 'validError', true)
								}) 
							 }
						}
					});
				}).catch((e) => {
					// 错误信息
					app.message({
						type: 'warning',
						message: e,
						duration: 4000,
					});
					// 跳转到第一个验证失败的表单位置 - validError
					const validErrorIndex = this.formList.findIndex(formItem => { return formItem.validError });
					this.toQuestion(validErrorIndex + 1);
				})
			},
			scoreCount(){
				let str = ""
				let jumpIndex = -1;
				this.formList.forEach((e,i)=>{
					if(e.flag=='9'){
						let n = 0
						e.nodes.forEach(o=>{
							n += Number(o.score)
						})
						if(n<100 || e.nodes.length===0) {
							this.$set(this.formList[i], 'validError', true);
							if(jumpIndex==-1){
								jumpIndex = i
							}
							str += `复合题No.${i+1}、`;
						}
					}
				})
				if(str.length>0){
					str = str.substring(0,str.length-1)
					app.confirm({ // 点击确认按钮的回调
						title: '提示',
						content: `${str}中子题分数占比总和不满100或至少要有一个子题，请更改`,
						type: 'warning',
						isShowCancelBtn: false,
						okText: '确定',
						onOk: function () {},
						onCancel: function () {} // 点击取消按钮的回调
					});
					this.toQuestion(jumpIndex+1);
				}

				return str.length>0
			},
			save(){
				if(this.formList.length==0){
					app.message({
						type: 'warning', 
						message: '不能直接保存，请添加试题!',
						duration:4000,
					});
					return
				}

				this.validateAll().then(()=>{
					//如果有复合题子题总分数占比不足100
					if(this.scoreCount()){
						return
					}

					const objArr = this.beforeSave();
					
						
					//for(const e of objArr){
					//	for(const k in e){
					//		if(k=='answer ||' ){
					//			e[k] = e[k]
					//			
					//			
					//			
					//		}
					//	}
					//	
					//	
					//	
					//	
					//	
					//}
					
					
					
					this.$http.post(`${this.baseUrl}/${this.saveUrl}`, objArr).then((res) => {
						if(!res.data.success){
							// 提示信息
							app.confirm({ // 点击确认按钮的回调
							    title: '提示',
							    content: `${res.data.message}`,
								type: 'warning',
								isShowCancelBtn: false,
								okText: '确定',
							    onOk: function () {},
							    onCancel: function () {} // 点击取消按钮的回调
							});
							// 跳转到第一个错误试题
							 if(res.data.numList){
								this.toQuestion(res.data.numList[0]);
								res.data.numList.forEach(i=>{
									this.$set(this.formList[i-1], 'validError', true)
								}) 
							 }
						}else{
							const _this = this
							app.confirm({
							    title: '提示',
							    content: `您已成功新增${objArr.length}道题`,
								type: 'success',
							    okText: '确定',
							    isShowCancelBtn: false, // 不显示取消按钮
							    onOk: function() { // 点击确认按钮的回调
									_this.formList.splice(0); // 清空试题对象
									_this.allGroupData = null; // 清空试题分组序号信息
								}
							});
						}
					});
				}).catch((e)=>{
					// 错误信息
					app.message({
						type: 'warning', 
						message: e,
						duration:4000,
					});
					// 跳转到第一个验证失败的表单位置 - validError
					const validErrorIndex = this.formList.findIndex(formItem => {return formItem.validError});
					this.toQuestion(validErrorIndex+1);
				})
			},
			beforeSave(isSave=true){
				const objArr = []
				this.formList.forEach(item=>{
					const obj =  this.copyVo(item)
					//1.如果是复合题
					obj.fileList = []
					if(obj.nodes){
						for(const o of obj.nodes){
							o.fileList = [];
							Object.assign(o,this.filterKatex(o,obj))
							o.outlineIds = obj.outlineIds
							o.knowledgeId = obj.knowledgeId
							//翻译题题干(复合题)
							if(o.flag=="8"){
								this.formatTranslate(o)
							}
							//多选题格式化答案
							this.formatAnswer(o)
						}
					}

					// 下载时，出题人更改
					if (!isSave) {
						const authorVo = this.userAllMap[obj.author];
						obj.author = `${authorVo.name}_${authorVo.sfzhLL}`;
					}
					//2.如果是普通题
					objArr.push(Object.assign(obj,this.filterKatex(obj)))
					//翻译题题干
					if(obj.flag=="8"){
						this.formatTranslate(obj)
					}
					//多选题格式化答案
					this.formatAnswer(obj)
				})
				return objArr;
			},
				
			/* 过滤试题答案 */
			formatAnswer(obj){
				if(obj.flag=="1" || obj.flag=="2"){
					if( app.isArray(obj.answer)){
						const sortArr = obj.answer.sort()
						obj.answer = sortArr.join('')
					}
				}
				//如果是填空题(人工判卷)--3//填空题(自动判卷)--4
				if(obj.flag=="3" || obj.flag=="4"){
					// 筛选答案
					let answer = [],
					divElem = document.createElement('div');
					divElem.innerHTML = obj.answer;
					let answerEmArr = $(divElem).find('em.paper-replace-em');
					answer = Array.from(answerEmArr).map(emItem => {
						emItem.removeAttribute('style')
						emItem.removeAttribute('contenteditable')
						emItem = emItem.outerHTML
						return emItem
					})
					obj.answer = answer.join(',');
					// 填空题 - 自动判卷（过滤掉所有的html标签）
					obj.flag == "4" && (obj.answer = obj.answer.replace(/<[^>]+>/g, ''));
					// 填空题 - 人工判卷 （去掉除img外的所有html标签）
					obj.flag == "3" && (obj.answer = obj.answer.replace(/<(?!\/?img|\/?em)[^<>]*>/ig, ''));
				}
			},
			/* 过滤翻译题 */
			formatTranslate(obj,noIndex){
				let strTitle = '',strAnswer = '',remark = '',remarkIsEmpty='';
				const column = obj.columnNum,
				isHasMark = obj.titleArr.findIndex(item => item.remark) >= 0;
				obj.titleArr.forEach((el,i)=>{
					if(el.title){
						const elTitle = el.title.trim(), elAnswer = el.answer.trim(), elRemark = el.remark.trim();
						let index = (i+1)+'.'
						if(noIndex){
							index = ''
						}
						if((++i)%column==0){
							elTitle && (strTitle += `${index}${elTitle}\n`);
							elAnswer && (strAnswer += `${index}${elAnswer}\n`);
							(elRemark || isHasMark)  && (remark += `${index}${elRemark}\n`);
							remarkIsEmpty += elRemark;
						}else{
							if(i<=obj.titleArr.length-1){
								elTitle && (strTitle += `${index}${elTitle}|||`);
								elAnswer && (strAnswer += `${index}${elAnswer}|||`);
								(elRemark || isHasMark)  && (remark += `${index}${elRemark}|||`);
								remarkIsEmpty += elRemark;
							}else{
								elTitle && (strTitle += `${index}${elTitle}`);
								elAnswer && (strAnswer += `${index}${elAnswer}`);
								(elRemark || isHasMark)  && (remark += `${index}${elRemark}`);
								remarkIsEmpty += elRemark;
							}
						}
					}
				})
				
				obj.answer = strAnswer.endsWith("|||")?strAnswer.substr(0,strAnswer.length-3):strAnswer;
				obj.title = strTitle.endsWith("|||")?strTitle.substr(0,strTitle.length-3):strTitle;
				obj.remark = !remarkIsEmpty?'':remark.endsWith("|||")?remark.substr(0,remark.length-3):remark;
				
			},
			/* 过滤公式 */
			filterKatex(item,pItem){
				const fieldMap = ['title','option1','option2','option3','option4','option5','option6','answer','remark']
				const obj = {}
				const div = document.createElement('div')
				for(const k of fieldMap){
					if(!item[k]){continue}//如果是空值不执行
					if(k=='answer' && (item.flag == '1' || item.flag=='2')){continue}
					if(k=='answer' && item.flag == '3') {
						div.innerHTML = item[k].replace(/<(?!\/?img|\/?em)[^<>]*>/ig, '');
					} else {
						div.innerHTML = item[k];
					}

					const list = div.querySelectorAll('img')
					const codeMap = {}
					list.forEach((e,index)=>{
						let mathTxt = e.getAttribute('data-mathtxt')
						if(!mathTxt){
							if(e.src.startsWith('data:image/svg+xml;base64')){
								for(const o of this.expression.arrayMap){
									if(o.src==e.src){
										mathTxt = o.code
									}
								}
							}
						}
						
						if(mathTxt){
							const em = document.createElement('em')
							em.classList.add('data-mathtxt')
							const text = document.createTextNode(  '$_latex_$_key'+index)
							em.appendChild(text)
							//记录公式位置
							codeMap['key'+index] = '$'+mathTxt+'$'
							//替换图片
							e.parentNode.insertBefore(em,e)
							e.remove()
						} else{
							if(!e.getAttribute('height') && e.naturalHeight>80){
								e.height = 80;
							}
							const srcArr = e.src.split('/')
							const  fingerprint = srcArr[srcArr.length-1].split('.')[0]
							//只向父级的fileList放入
							if(fingerprint && this.fileMap[fingerprint]){
								if(pItem){
									pItem.fileList.push(this.fileMap[fingerprint])
								}else{
									item.fileList.push(this.fileMap[fingerprint])
								}
							}
						}
					})
					let c = div.innerHTML
					for(const k in codeMap){
						c = c.replace('$_latex_$_'+k,codeMap[k])
					}
					obj[k] = c.replace(/<\/p><p>/g, '\n').replace(/<[\/]*p>/g, '');
				}
				item.fileList = Array.from(new Set(item.fileList))
				return obj;
			},
			changeImportFile(fileList) { // 成功导入文件
				const response = fileList[0].response;
				this.handleImportData(response); // 数据处理
				this.computedQuestionGroup(); // 试题分组计算
			},
			constructEditorData(item) {
			    const fieldMap = ['title', 'option1', 'option2', 'option3', 'option4', 'option5', 'option6', 'answer', 'remark']
			    for (const k of fieldMap) {
			        if (!item[k]) { continue }//如果是空值不执行
			        if (k == 'answer' && (item.flag == '0' || item.flag == '1' || item.flag == '2' || item.flag == '3' || item.flag == '4')) { continue }
			        // 将编辑器的\n 替换为 </p><p>
			        const itemData = `<p>${item[k].replace(/<[\/]*p>/g, '')}</p>`;
			        if (item[k].match(/\n/g) && item[k].match(/\n/g).length > 0) { // 多个\n
			            item[k] = itemData.replace(/\n/g, '</p><p>');
			        } else {
			            item[k] = itemData;
			        }

			    }
			},
			/* 导入文件后的回调 */
			handleImportData(res) {
				if (res && res.success) {
					const formList = res.data,
					importQuestionIndex = this.formList.length + 1;
					// 合并试题
					this.formList = this.formList.concat(formList);
					// 翻译题对象修改
					formList.forEach(formItem => {
						Object.assign(formItem, {
							_id: '_' + parseInt(Math.random() * 100000000),
							//knowledgeId: formItem.knowledgeId,
							//outlineIds: formItem.outlineIds,
							isImport: true
						});
						this.constructQuestionData(formItem);
						// formItem._id = '_' + parseInt(Math.random() * 100000000);
						// formItem.knowledgeId = knowledgeId;
						// formItem.flag == '7' && this.toProgramData(formItem);
						if (formItem.flag == '9') {
							formItem.nodes.forEach(cFormItem => {
								Object.assign(cFormItem, {
									_id: '_' + parseInt(Math.random() * 100000000),
									_pId: formItem._id,
									//knowledgeId: cFormItem.knowledgeId,
									isImport: true
								});
								this.constructQuestionData(cFormItem);
								// cFormItem.flag == '8' && this.toTranslateData(cFormItem);
								// ['3', '4'].includes(cFormItem.flag) && this.toBlankData(cFormItem);
								// cFormItem.flag == '7' && this.toProgramData(cFormItem);
								this.$set(this.compoundMap, cFormItem._id, cFormItem);
							});
							this.$set(this.compoundMap, formItem._id, formItem);
						}
					});
					// this.isFocus = formList[0]._id;
					// 跳转到刚导入的第一道试题
					const timeVo = setTimeout(() => {
						this.toQuestion(importQuestionIndex);
						clearTimeout(timeVo);
					}, 2000);
				} else {
					app.loading.hide();
				}
			},
			constructQuestionData(data) { // 构造数据
				// 修改所有试题中的编辑器数据
				['0', '1', '2', '3', '4', '5','9'].includes(data.flag) && this.constructEditorData(data);
				// 翻译题 数据重构
				data.flag == '8' && this.toTranslateData(data);
				// 填空题 数据重构
				this.$nextTick(() => {
					['3', '4'].includes(data.flag) && this.toBlankData(data);
				});
			},
			/*填空题转换*/
			toBlankData(formItem) {
				// 答案转化
				// 填空题 题干中有公式时，转为图片
				const titleCopy = formItem.title;
				if (formItem.flag == '3') { // 人工判卷
					// 1、获取答案
					const answerArr = [],
						div = document.createElement('div'),
						answerHtml = $(div).append(formItem.answer);
					// 2、构造答案
					let answerCopy = titleCopy;
					const titleEmArr = $(titleCopy).find('em.paper-replace-em'),
						answerEmArr = answerHtml.find('em.paper-replace-em');
					if (answerEmArr.length > 0) {
						for (let i = 0; i < answerEmArr.length; i++) {
							answerEmArr[i].setAttribute('contentEditable', true);
							const data = answerCopy.replace(titleEmArr[i].outerHTML, answerEmArr[i].outerHTML);
							answerCopy = data;
						}
					}
					
					formItem.answer = answerCopy;
					
				} else if (formItem.flag == '4') {
					let answerCopy = titleCopy;
					const titleEmArr = $(titleCopy).find('em.paper-replace-em');
					if (titleEmArr.length > 0) {
						formItem.answer = answerCopy.replace(titleEmArr[0].innerHTML, formItem.answer);
					}
				}
			},
			mathToImg(elem, mathArr, cb) { // 公式字符转标签转图片
				return this.typeset(() => [elem]).then((e) => {
					const allFormulaElems = $(elem).find('mjx-container');
					for (let i = 0; i < allFormulaElems.length; i++) {
						const img = app.svgToImg(allFormulaElems[i].querySelector('svg'), allFormulaElems.length > 0 && allFormulaElems[i].innerText);
						img.setAttribute('data-mathtxt', mathArr[i]);
						allFormulaElems[i].parentNode.replaceChild(img, allFormulaElems[i]);
					}
					cb && cb(elem.innerHTML);
				});
			},

			typeset(code) { // 公式字符串转公式标签
				return (
					Promise.resolve() // 公式Promise对象
						.then(() => {
							let e = code();
							MathJax.typesetPromise(e);
							return e[0];
						})
						//.then(() => MathJax.typesetPromise(code()))
						.then((e) => {
							e.innerHTML = window.marked.parse(e.innerHTML || "");
							return e;
						})
						.catch((err) => console.log("Typeset failed: " + err.message))
				);
			},
			/* 翻译题转换 */
			toTranslateData(formItem) {
				// 构造翻译题titleArr字段数组
				if (formItem.title) {
					// 1. 按行分组
					// 将题干分组 (\n)
					const titleRow = formItem.title.split('\n'),
						// 将答案分组 (\n)
						answerRow = formItem.answer ? formItem.answer.split('\n') : [],
						// 将解析分组 (\n)
						remarkRow = formItem.remark ? formItem.remark.split('\n') : [];
					!titleRow[titleRow.length - 1] && titleRow.splice(titleRow.length - 1, 1);

					// 2. 循环行
					// 模式设置 （解析模式数量 和 题干数量 相等为 解析模式；否则为常规模式）
					// 一行几题 （任何一行含有 ||| 为一行两列，否则为一行一列）
					const rowsNum = titleRow.length;
					this.$set(formItem, 'titleArr', []);
					let questionNum = 0;
					for (let i = 0; i < rowsNum; i++) {
						if (titleRow[i].includes('|||')) {
							// 1. 有则将 题干、答案、解析 拆为数组
							const titleRowArr = titleRow[i] ? titleRow[i].split('|||') : [],
								answerRowArr = answerRow[i] ? answerRow[i].split('|||') : [],
								remarkRowArr = remarkRow[i] ? remarkRow[i].split('|||') : [];
							// 1.1 构造数组
							questionNum++;
							this.generateTransTitle(questionNum, formItem, titleRowArr[0], answerRowArr[0], remarkRowArr[0]);
							questionNum++;
							this.generateTransTitle(questionNum, formItem, titleRowArr[1], answerRowArr[1], remarkRowArr[1]);
						} else {
							// 2. 无则直接构造数组
							questionNum++;
							this.generateTransTitle(questionNum, formItem, titleRow[i], answerRow[i], remarkRow[i]);
						}
						// 3. 设置一行几列
						if (i == 0) {
							this.$set(formItem, 'columnNum', titleRow[0].includes('|||') ? 2 : 1);
						}
					}

					// 3. 设置模式
					// 获取所有解析
					const nullRmarkVo = formItem.titleArr.find(item => item.remark.replace(/\s+/g, ''));
					this.$set(formItem, 'remarkMode', nullRmarkVo ? '1' : '0');

					//     const titleArr = formItem.title.split('|||'),
					//         answerArr = formItem.answer ? formItem.answer.split('|||') : [],
					// 		remarkArr = formItem.remark ? formItem.remark.split('|||') : [];
					// 	// 模式和一行几题
					//     this.$set(formItem, 'remarkMode', remarkArr.length > 0 ? '1' : '0');
					//     this.$set(formItem, 'columnNum', formItem.title.includes('|||') ? 2 : 1);
					//     this.$set(formItem, 'titleArr', []);
					//     titleArr.forEach((titleItem, index) => {
					//         let titleRowArr = titleItem.split('\n'),
					//             answerRowArr = answerArr[index] && answerArr[index].split('\n'),
					//             remarkRowArr = (remarkArr && remarkArr[index]) ? remarkArr[index].split('\n') : '';
					//         titleRowArr.forEach((cTitleItem, cIndex) => {
					//             formItem.titleArr.push({
					//                 title: cTitleItem,
					//                 answer: answerRowArr ? answerRowArr[cIndex] : '',
					//                 remark: (remarkRowArr.length > 0) ? remarkRowArr[cIndex] : ''
					//             });
					//         })
					//     });
				}
			},
			generateTransTitle(rowNum, formItem, titleStr, answerStr, remarkStr) { // 构造翻译题数组
				// 正则替换 序号
				formItem.titleArr.push({
					title: titleStr ? titleStr.replace(`${rowNum}.`, '') : '',
					answer: answerStr ? answerStr.replace(`${rowNum}.`, '') : '',
					remark: remarkStr ? remarkStr.replace(`${rowNum}.`, '') : '',
					isAddRemark: remarkStr ? (remarkStr.replace(`${rowNum}.`, '') ? true : false) : false
				});
			},
			/* 重置 */
			reset(){
				if(this.isFocus){
					const that = this;
					let prefix = '';
					let e = this.formList.find((e,i)=>{
						if(e._id==this.isFocus){
							prefix = '试题'+(i+1)+'.';
						}
						return e._id==this.isFocus;
					})
					
					if(!e){
						e = this.compoundMap[this.isFocus]
						//如果是复合题子题
						if(e){
							//找到本题序号
							this.formList.find((el,i)=>{
								if(el._id==e._pId){
									prefix = '复合题子题'+(i+1)+'.';
									el.nodes.forEach((item,j)=>{
										if(item._id==e._id){
											prefix += j+1+'.';
										}
									})
								}
								return el._id==e._pId;
							})
							
						}
					}
					if(e){
						// 1、滚动到具体重置的试题
						const resetQuestionId = `question-item-${e._id}`;
						document.getElementById(resetQuestionId) && document.getElementById(resetQuestionId).scrollIntoView();
						// 2、重置
						const flagAliasName =  this.flagAliasMap[e.flagAlias]
						
						const timeVo = setTimeout(() => {
							app.confirm({
								title: '提示',
								content: `正在重置<<${prefix}${flagAliasName || ''}>>请确认`,
								type: 'warning',
								okText: '确认',
								cancelText: '取消',
								onOk: function() {
									that.doReset(e)
								}, // 点击确认按钮的回调
								onCancel: function() {
								} // 点击取消按钮的回调
							});
							clearTimeout(timeVo);
						});
					}
					
				}
			},
			doReset(e){
				let flag = e.flag;
				let flagAlias = e.flagAlias;
				let nodes = e.flag=='9' ? [] : null;
				const tmpO = this.copyVo(this.vo)
				tmpO.flag = flag;
				tmpO.flagAlias = flagAlias;
				tmpO.nodes = nodes;
				Object.assign(e,tmpO)
			},
			closePage() { // 关闭当前页
				if (window.opener && window.opener.vm) {
					window.opener.vm.listPage()
				}
				window.close();
			},
			changeEditorRange(data) { // 动态修改provide修改的变量
				Object.assign(this.editorRangeVo, data);
			},
			globalConfig() { // 全局配置
				const that = this,

				userId = app.getUser().id;
				!this.defaultConfigVo.author && (this.defaultConfigVo.author=userId);
				app.dialog.show(app.getRouteLinkpre() + 'admin/core/examcorequestion/components/global-config.html', {
					data() {
						const dv = JSON.parse(JSON.stringify(that.defaultConfigVo))
						return {
							userMap: that.userMap,
							defaultConfigVo: dv,
							isFocus: that.isFocus
						}
					},
					methods: {
						applyConfig() { // 应用配置
							// 默认配置值保存
							this.configData(this.defaultConfigVo);
						},
						applyFocusConfig() { // 应用焦点配置
							that.confirmDialog('你确定要使用焦点配置吗?')
							.then(() => {
								const currentData = that.formList.filter(formItem => formItem._id == this.isFocus);
								this.configData(currentData[0]);
							})
							.catch(() => {});
						},
						configData(data) {
							Object.assign(that.defaultConfigVo, data);
							Object.assign(that.vo, data);
							that.$Message.success('应用配置成功！');
							this.isShow = false;
						},
						reset() { // 重置
							Object.assign(this.defaultConfigVo, {
								outlineIds: [], // 试题大纲
								knowledgeId: '', // 知识点
								difficulty: '',//试题难度
								author: userId, // 出题人
							});
							Object.assign(that.vo, this.defaultConfigVo);
						}
					}
				});
			},
		},
	}
</script>